/**
 * 应用入口核心模块
 * @module app
 * @version 1.0.0
 * @author 技术架构组
 * @since 2024-01-01
 * @description 作为整个服务的入口中枢，承担「组件整合+生命周期管理」核心职责，
 *              严格遵循「分层架构设计」，向上对接运维部署，向下统筹业务模块，
 *              是保障服务稳定性、可扩展性的关键节点
 * 
 * 所属架构分层：
 * - 层级定位：应用层（Application Layer）- 入口层
 * - 上层依赖：运维环境（容器/虚拟机）、环境变量配置
 * - 下层管理：中间件层、路由层、业务逻辑层
 * 
 * 核心技术栈：
 * - 基础框架：Express.js (v4.x+)
 * - 安全组件：JWT (express-jwt)、CORS
 * - 数据校验：Joi (v17.x+)
 * - 配置管理：环境变量驱动式配置
 * 
 * 环境依赖规范：
 * - 运行时：Node.js 16.x LTS (推荐) / 18.x LTS (兼容)，需开启ES6+特性支持
 * - 依赖管理：使用npm v8+ / yarn v1.22+ 锁定依赖版本（package-lock.json/yarn.lock）
 * - 部署环境：开发(development)、测试(test)、预发布(staging)、生产(production) 四环境隔离
 */

// ============================================================================
// 1. 核心依赖导入 - 按「功能优先级」排序，统一管理第三方依赖
// ============================================================================

/**
 * Express Web框架核心依赖
 * @description 服务端请求处理的基础载体，负责路由分发、中间件链管理、HTTP协议封装
 * 
 * 选型决策依据（企业级技术选型维度）：
 * 1. 轻量无侵入：仅提供核心能力（路由+中间件），不强制业务架构，适配从单体到微服务的演进
 * 2. 生态成熟度：具备完善的中间件生态（覆盖安全、日志、监控等场景），降低定制开发成本
 * 3. 性能适配：单线程异步IO模型，适合IO密集型业务（如API接口服务），QPS支持可达万级（优化后）
 * 4. 团队适配：学习成本低，文档完善，团队上手周期短，问题排查资源丰富
 * 
 * 生产环境增强方案：
 * - 性能优化：集成 `compression` 中间件开启Gzip压缩，降低传输带宽消耗
 * - 监控增强：接入 `express-status-monitor` 实时监控CPU/内存/请求延迟
 * - 多进程扩展：通过 `cluster` 模块启动多进程（数量=CPU核心数），充分利用多核资源
 * - 安全加固：禁用 `x-powered-by` 响应头（隐藏技术栈），避免攻击面暴露
 */
const express = require('express');

/**
 * Joi 数据校验库
 * @description 负责「请求入参合法性校验」，在业务逻辑执行前拦截无效数据，降低下游错误处理成本
 * 
 * 核心价值（企业级数据安全维度）：
 * 1. 声明式校验：支持复杂规则定义（如字符串格式、数值范围、嵌套对象校验），代码可读性高
 * 2. 错误聚合：自动聚合多字段校验错误，避免手动逐字段判断
 * 3. 安全前置：拦截恶意入参（如超长字符串、非法格式数据），减少SQL注入、内存溢出风险
 * 
 * 企业级最佳实践：
 * - 校验规则集中化：在 `./schema` 目录按业务模块拆分校验规则（如 user.schema.js、order.schema.js）
 * - 校验范围全覆盖：所有用户输入（query参数、path参数、body体、header头）必须经过校验
 * - 错误信息脱敏：生产环境返回「参数格式错误」通用提示，开发环境返回具体错误字段（避免暴露校验规则）
 * - 版本兼容：校验规则变更需同步更新文档，避免破坏下游调用（如前端适配）
 */
const joi = require('joi');

/**
 * 项目配置中心
 * @description 统一管理全环境配置，解决「硬编码」问题，支持环境差异化配置
 * 
 * 配置设计规范（企业级配置管理维度）：
 * 1. 分层配置：按「公共配置+环境配置」拆分，公共配置（如接口前缀）全局复用，
 *    环境配置（如数据库地址）通过 NODE_ENV 自动加载（dev/test/staging/prod）
 * 2. 敏感配置隔离：JWT密钥、数据库密码等敏感信息，通过「环境变量注入」（如process.env.JWT_SECRET），
 *    禁止写入代码仓库（.gitignore 排除配置文件）
 * 3. 配置分类：按功能模块划分配置项，便于维护：
 *    - 服务配置：端口（port）、请求超时时间（timeout）
 *    - 安全配置：JWT密钥（jwtSecretKey）、CORS白名单（corsWhitelist）
 *    - 外部依赖配置：数据库地址（db.host）、第三方API密钥（apiKey.xxx）
 * 4. 配置校验：启动时校验关键配置（如JWT密钥是否存在），缺失则终止启动（避免运行时异常）
 */
const config = require('./config');

/**
 * JWT认证中间件（express-jwt）
 * @description 负责「用户身份合法性校验」，基于Token实现无状态认证，适配前后端分离架构
 * 
 * 认证方案设计（企业级安全架构维度）：
 * 1. 认证流程：
 *    - 前端登录成功后获取Token，后续请求在Header携带 `Authorization: Bearer {token}`
 *    - 中间件解析Token，验证签名有效性+过期时间，通过则将用户信息挂载到 `req.auth`
 *    - 验证失败则抛出「UnauthorizedError」，由全局错误中间件统一处理
 * 
 * 安全设计要点：
 * - 加密算法：采用 HS256 对称加密（单服务端场景），多服务端场景建议切换为 RS256 非对称加密
 * - Token生命周期：访问Token有效期设为 2h（降低泄露风险），配合「刷新Token」机制（有效期7天）
 * - 密钥管理：JWT密钥每 90 天轮换一次，通过配置中心动态加载（无需重启服务）
 * - 异常拦截：Token篡改、过期、缺失等场景，返回统一错误码（如401），避免暴露具体原因（防枚举攻击）
 */
const { expressjwt: expressJwt } = require('express-jwt');

// ============================================================================
// 2. 应用实例初始化 - 单例模式，确保全局唯一的Express实例
// ============================================================================

/**
 * Express应用实例
 * @type {express.Application}
 * @description 服务的「核心载体」，所有中间件、路由均挂载于此，管理请求处理全链路
 * 
 * 实例配置规范（企业级实例管理维度）：
 * 1. 环境标识：启动时通过 `app.set('env', process.env.NODE_ENV)` 明确环境，
 *    生产环境自动启用错误信息脱敏（避免暴露堆栈）
 * 2. 安全加固：
 *    - app.disable('x-powered-by')：隐藏响应头中的「X-Powered-By: Express」，避免技术栈探测
 *    - app.set('trust proxy', true)：若部署在反向代理（如Nginx）后，信任代理IP，确保req.ip获取准确
 * 3. 资源限制：
 *    - 配置请求体最大体积：app.use(express.json({ limit: '100kb' }))，防止大Payload攻击
 *    - 配置路由匹配优先级：按「精确路由→前缀路由」顺序挂载，避免路由冲突
 */
const app = express();

// ============================================================================
// 3. 全局中间件配置 - 按「请求处理流程」排序（先过滤→再解析→再认证）
// ============================================================================

/**
 * CORS跨域中间件
 * @description 解决前后端分离架构中的「跨域资源访问」问题，严格控制跨域权限
 * 
 * 跨域配置规范（企业级安全维度）：
 * 1. 环境差异化配置：
 *    - 开发环境：origin: '*'（简化调试，避免本地开发跨域报错）
 *    - 生产环境：origin: config.corsWhitelist（仅允许白名单域名，如官网、管理后台）
 * 2. 细粒度权限控制：
 *    - methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']（仅开放业务必需的HTTP方法）
 *    - allowedHeaders: ['Content-Type', 'Authorization']（仅允许必要请求头）
 *    - credentials: true（如需跨域携带Cookie，需同时配置前端withCredentials: true）
 *    - maxAge: 86400（预检请求缓存24小时，减少OPTIONS请求次数，降低服务压力）
 * 3. 安全风险规避：
 *    - 禁止生产环境使用 origin: '*' + credentials: true（浏览器会拒绝该配置，且存在安全风险）
 *    - 白名单域名使用「精确匹配」（如https://admin.example.com，避免通配符滥用）
 */
const cors = require('cors');
app.use(cors(
  process.env.NODE_ENV === 'development' 
    ? { origin: '*' } 
    : { 
        origin: config.corsWhitelist,
        methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
        allowedHeaders: ['Content-Type', 'Authorization'],
        credentials: true,
        maxAge: 86400
      }
));

/**
 * 请求体解析中间件
 * @description 解析HTTP请求体数据，转换为JSON格式供下游使用，支持两种主流格式：
 *              1. application/x-www-form-urlencoded（表单提交）
 *              2. application/json（接口请求常用）
 * 
 * 解析配置规范（企业级数据处理维度）：
 * 1. 格式适配：
 *    - express.urlencoded({ extended: false })：使用Node内置querystring模块解析，不支持嵌套对象
 *    - 若需解析嵌套结构（如{ user: { name: 'xxx' } }），需设为 extended: true（依赖qs模块）
 * 2. 安全限制：
 *    - 配置 limit: '100kb'（生产环境）：限制请求体最大体积，防止恶意提交大文件导致内存溢出
 *    - 忽略无效格式：对Content-Type不匹配的请求（如text/plain），不进行解析，避免垃圾数据
 * 3. 错误处理：解析失败（如JSON格式错误）会触发400错误，由全局错误中间件捕获处理
 */
app.use(express.urlencoded({ extended: false, limit: '100kb' }));
// 补充JSON格式解析（企业级接口常用格式，需显式配置）
app.use(express.json({ limit: '100kb' }));

/**
 * 响应工具全局挂载 - 统一API响应格式
 * @description 封装 res.cc 方法，解决「响应格式不统一」问题，降低前后端协作成本
 * 
 * 统一响应格式设计（企业级API规范维度）：
 * 1. 响应结构定义：
 *    - 成功响应：{ status: 0, message: '操作成功', data: {} }
 *    - 失败响应：{ status: 1, message: '操作失败', errorCode: 'XXX' }（可选errorCode用于细粒度错误处理）
 * 2. 方法功能：
 *    - 入参1：err - 错误信息（支持Error对象/字符串，Error对象自动取message属性）
 *    - 入参2：status - 业务状态码（0=成功，1=失败，默认1）
 *    - 入参3：data - 业务数据（成功时返回，默认undefined）
 * 3. 扩展建议：
 *    - 增加「请求ID」字段：在响应头添加 X-Request-ID，便于链路追踪（配合日志系统）
 *    - 支持国际化：根据请求头 Accept-Language 返回多语言提示（如中文/英文）
 */
app.use((req, res, next) => {
  /**
   * 统一响应工具方法
   * @param {Error|string} err - 错误信息/Error对象
   * @param {number} [status=1] - 业务状态码（0=成功，1=失败）
   * @param {object} [data=null] - 成功时返回的业务数据
   */
  res.cc = function (err, status = 1, data = null) {
    res.send({
      status,
      message: err instanceof Error ? err.message : err,
      ...(data && { data }) // 仅当data存在时才返回该字段
    });
  };
  next(); // 传递中间件链，避免阻塞请求
});

/**
 * JWT认证中间件挂载 - 按「路由前缀」实现权限隔离
 * @description 对非公开接口强制认证，公开接口（如登录、注册）通过 unless 配置豁免
 * 
 * 认证范围设计（企业级权限维度）：
 * 1. 豁免规则：通过正则匹配 `/^\/api\//` 前缀路由（公开接口），无需认证，
 *    如 /api/user/login（登录）、/api/user/register（注册）
 * 2. 认证范围：所有 `/my/` 前缀路由（用户个人中心接口）必须认证，
 *    如 /my/userinfo（获取用户信息）、/my/order（查询订单）
 * 3. 扩展配置：
 *    - requestProperty: 'user'：将解析后的Token数据挂载到 req.user（默认req.auth，可自定义）
 *    - credentialsRequired: false：允许无Token请求（需下游手动判断，适合「可选认证」场景）
 * 
 * 注意事项：
 * - 中间件挂载顺序：需在「请求体解析」之后、「路由挂载」之前，确保Token能被正确解析
 * - 多角色认证：若需区分管理员/普通用户，可在该中间件后添加「角色校验中间件」
 */
app.use(expressJwt({
  secret: config.jwtSecretKey, // 从配置中心读取密钥，避免硬编码
  algorithms: ['HS256'], // 明确指定加密算法（express-jwt v7+ 强制要求）
  credentialsRequired: true // 无Token时直接返回401，不允许匿名访问
}).unless({ path: [/^\/api\//] })); // 公开接口豁免列表

// ============================================================================
// 4. 业务路由模块化挂载 - 按「业务域」拆分，实现关注点分离
// ============================================================================

/**
 * 用户公共路由模块（/api 前缀）
 * @description 处理公开的用户相关接口（登录、注册、密码重置），无需认证
 * 
 * 路由挂载规范（企业级路由设计维度）：
 * 1. 前缀统一：所有路由挂载到 `/api` 前缀，与认证豁免规则匹配
 * 2. 模块拆分：路由逻辑与业务逻辑分离，路由仅负责「请求分发」，
 *    业务逻辑由 `./router_handler/user` 实现（如userHandler.login）
 * 3. 命名规范：路由文件名按「业务域+router」命名（如 user.router.js），便于检索
 */
const userRouter = require('./router/user');
app.use('/api', userRouter);

/**
 * 用户个人中心路由模块（/my 前缀）
 * @description 处理需认证的用户私有接口（个人信息、订单管理），强制JWT认证
 * 
 * 路由设计要点：
 * 1. 权限对齐：路由前缀 `/my` 与JWT认证范围对齐，确保所有接口需认证
 * 2. 业务内聚：所有与「用户个人数据」相关的接口集中在此模块，
 *    如 /my/userinfo（获取信息）、/my/userinfo/update（更新信息）
 * 3. 版本管理：若后续需迭代接口，可通过「版本前缀」扩展（如 /my/v2/userinfo），
 *    实现新旧版本兼容
 */
const userinfoRouter = require('./router/userinfo');
app.use('/my', userinfoRouter);

const artCateRouter = require('./router/artcate')
app.use('/my/article',artCateRouter)

// ============================================================================
// 5. 全局错误处理中间件 - 统一捕获全链路错误，确保服务稳定
// ============================================================================

/**
 * 全局错误处理中间件
 * @description 捕获「中间件链+业务逻辑」中抛出的所有错误，按错误类型统一处理，
 *              避免裸抛错误导致服务崩溃或返回不规范响应
 * 
 * 错误处理规范（企业级稳定性维度）：
 * 1. 错误分类处理（按优先级排序）：
 *    a. Joi数据校验错误：err instanceof joi.ValidationError
 *       - 开发环境：返回具体错误字段（err.details[0].message），便于调试
 *       - 生产环境：返回「请求参数格式错误」，避免暴露校验规则（防攻击）
 * 
 *    b. JWT认证错误：err.name === 'UnauthorizedError'
 *       - 统一返回「身份验证失败，请重新登录」，不区分具体原因（如Token过期/篡改），
 *         避免攻击者通过错误信息枚举漏洞
 * 
 *    c. 业务自定义错误：err instanceof BusinessError（需自定义错误类）
 *       - 返回业务错误信息（如「用户已存在」），配合业务状态码使用
 * 
 *    d. 未知错误（如数据库异常、代码逻辑错误）
 *       - 生产环境：返回「服务器内部错误」，避免暴露堆栈信息
 *       - 开发环境：打印错误堆栈（err.stack），便于定位问题
 *       - 日志记录：强制写入错误日志（如通过winston/pm2日志），包含请求上下文（路径、参数、IP）
 * 
 * 2. 响应状态码规范：
 *    - 400：请求参数错误（Joi校验失败）
 *    - 401：身份认证失败（JWT错误）
 *    - 403：权限不足（如普通用户访问管理员接口）
 *    - 404：接口不存在（需配合404中间件）
 *    - 500：服务器内部错误（未知异常）
 * 
 * 3. 扩展建议：
 *    - 接入错误监控平台（如Sentry），实时告警线上错误
 *    - 增加「请求ID」关联错误日志，便于链路追踪（从请求头获取X-Request-ID）
 */
app.use((err, req, res, next) => {
  // 1. Joi数据校验错误
  if (err instanceof joi.ValidationError) {
    const errorMsg = process.env.NODE_ENV === 'development' 
      ? err.details[0].message : err
      //: '请求参数格式错误，请检查输入';
    return res.cc(errorMsg, 1);
  }

  // 2. JWT认证错误
  if (err.name === 'UnauthorizedError') {
    return res.cc('身份验证失败，请重新登录', 1);
  }

  // 3. 业务自定义错误（示例：需先定义BusinessError类）
  // if (err instanceof BusinessError) {
  //   return res.cc(err.message, err.statusCode);
  // }

  // 4. 未知错误 - 记录日志+返回通用提示
  console.error(`[全局错误捕获] | 请求路径: ${req.path} | IP: ${req.ip} | 错误信息: `, err.stack);
  res.cc(process.env.NODE_ENV === 'development' ? err.message : '服务器内部错误', 1);
});

// ============================================================================
// 6. 服务启动与生命周期管理 - 确保「优雅启动+优雅关闭」
// ============================================================================

/**
 * 启动HTTP服务
 * @description 监听指定端口，启动服务，打印启动日志，注册进程事件确保优雅关闭
 * 
 * 服务启动规范（企业级运维维度）：
 * 1. 端口配置：
 *    - 开发环境：固定端口3007（避免与其他本地服务冲突）
 *    - 生产环境：优先读取环境变量 process.env.PORT，无则使用配置中心默认端口（如3000）
 *    - 端口占用处理：启动前检查端口是否被占用，避免启动失败（可集成 portfinder 库）
 * 
 * 2. 启动日志：
 *    - 开发环境：打印「服务启动地址」（http://127.0.0.1:3007），便于快速访问
 *    - 生产环境：打印「服务启动时间+环境标识+端口」，如「2024-05-20 14:30:00 | production | port: 3000」
 * 
 * 3. 优雅关闭（生产环境必需）：
 *    - 监听进程退出信号（SIGINT、SIGTERM），如 Ctrl+C、容器停止命令
 *    - 关闭前执行「资源释放」逻辑：数据库连接关闭、定时器清除、未完成请求处理
 *    - 避免强制退出导致数据丢失（如未提交的数据库事务）
 * 
 * 4. 高可用增强：
 *    - 进程管理：使用PM2启动服务（pm2 start app.js），实现「崩溃自动重启+日志轮转」
 *    - 健康检查：添加 /health 接口（返回 { status: 'up' }），供负载均衡器检测服务状态
 *    - 多实例部署：在多台机器/容器启动服务，通过负载均衡（如Nginx）分发请求
 */
const port = process.env.PORT || 3007; // 优先使用环境变量端口，其次默认3007
app.listen(port, () => {
  const env = process.env.NODE_ENV || 'development';
  console.log(`[服务启动成功] | 环境: ${env} | 地址: http://127.0.0.1:${port} | 时间: ${new Date().toLocaleString()}`);
});

// 生产环境 - 注册进程优雅关闭事件
if (process.env.NODE_ENV === 'production') {
  // 捕获 Ctrl+C 信号（SIGINT）
  process.on('SIGINT', () => {
    console.log(`[服务开始关闭] | 时间: ${new Date().toLocaleString()}`);
    // 1. 释放资源（示例：关闭数据库连接）
    // const db = require('./db');
    // db.close().then(() => {
    //   console.log('数据库连接已关闭');
    //   process.exit(0); // 正常退出
    // }).catch(err => {
    //   console.error('数据库连接关闭失败:', err);
    //   process.exit(1); // 异常退出
    // });

    // 简化版：无资源需释放时直接退出
    process.exit(0);
  });

  // 捕获容器停止信号（SIGTERM）
  process.on('SIGTERM', () => {
    console.log(`[服务收到停止信号] | 时间: ${new Date().toLocaleString()}`);
    process.exit(0);
  });
}